package cz.stv.pecinovsky.město;

import cz.stv.pecinovsky.správceplátna.Kreslítko;
import cz.stv.pecinovsky.správceplátna.SprávcePlátna;

import cz.stv.pecinovsky.utility.Barva;
import cz.stv.pecinovsky.utility.Pozice;
import cz.stv.pecinovsky.utility.Rozměr;


import static cz.stv.pecinovsky.utility.Směr8.*;


/*******************************************************************************
 * Instance třídy {@code Okruh} představují okružní trasy,
 * po kterých je možné cestovat nebo závodit.
 *
 * @author  Rudolf PECINOVSKÝ
 * @version 1.10.2678 — 2011-10-27
 */
public class Okruh implements IModulový
{
//== KONSTANTNÍ ATRIBUTY TŘÍDY =================================================

    /** Plátno, na které se bude instance kreslit. */
    private static final SprávcePlátna SP = SprávcePlátna.getInstance();



//== PROMĚNNÉ ATRIBUTY TŘÍDY ===================================================
//== STATICKÝ INICIALIZAČNÍ BLOK - STATICKÝ KONSTRUKTOR ========================
//== KONSTANTNÍ ATRIBUTY INSTANCÍ ==============================================
//== PROMĚNNÉ ATRIBUTY INSTANCÍ ================================================

    /** Počáteční blok cesty. Je-li cesta uzavřená, je shodný s koncovým. */
    private PoleCesty počátek;

    /** Relativní políčková pozicePole počátku vůči pozici obdélníka
     *  definujícího zabranou oblast. */
    Pozice rpPozicePočátku = null;

    /** Políčkový rozměr okruhu je definován rozměrem nejmenšího obdélníku,
     *  do kterého se okruh vejde. */
    Rozměr pRozměr = null;

    /** Bodová pozicePole oblasti zaujímané okruhem. */
    private Pozice poziceOkruhu;




//== PŘÍSTUPOVÉ METODY VLASTNOSTÍ TŘÍDY ========================================
//== OSTATNÍ NESOUKROMÉ METODY TŘÍDY ===========================================

    /***************************************************************************
     * Vytvoří na zadané pozici nejmenší možný okruh.
     * Okruh je čtvercový, má implicitní, tj. šedou barvu
     * a vyráží se z levého horního rohu směrem na východ.
     *
     * @param startPozice Pozice startovního pole okruhu
     * @return Vytvořený okruh
     */
    public static Okruh čtverec(Pozice startPozice)
    {
        StavitelOkruhů stavitel = new StavitelOkruhů(startPozice);
        stavitel.zahajNa   (VÝCHOD);
        stavitel.pokračujNa(JIH);
        stavitel.pokračujNa(ZÁPAD);
        stavitel.uzavřiNa  (SEVER);
        return stavitel.getOkruh();
    }


    /***************************************************************************
     * Vytvoří na zadané pozici malý okruh zadané barvy ve tvaru velkého L.
     * Po okruhu se vyráží z levého horního rohu směrem na jih.
     *
     * @param startPozice Pozice startovního pole okruhu
     * @param barva       Barva budovaného okruhu
     * @return Vytvořený okruh
     */
    public static Okruh elko(Pozice startPozice, Barva barva)
    {
        StavitelOkruhů stavitel = new StavitelOkruhů(startPozice, barva);
        stavitel.zahajNa   (JIH);
        stavitel.pokračujNa(JIH);
        stavitel.pokračujNa(JIH);
        stavitel.pokračujNa(VÝCHOD);
        stavitel.pokračujNa(VÝCHOD);
        stavitel.pokračujNa(VÝCHOD);
        stavitel.pokračujNa(SEVER);
        stavitel.pokračujNa(SEVER);
        stavitel.pokračujNa(ZÁPAD);
        stavitel.pokračujNa(SEVER);
        stavitel.pokračujNa(ZÁPAD);
        stavitel.uzavřiNa  (ZÁPAD);
        return stavitel.getOkruh();
    }


//##############################################################################
//== KONSTRUKTORY A TOVÁRNÍ METODY =============================================

    /***************************************************************************
     * Na zadané pozici založí novou cestu implicitní barvy
     * vyrážející zadaným směrem a vytvoří její první blok.
     *
     * @param počátek  Pozice počátečního bloku
     */
    public Okruh(PoleCesty počátek)
    {
        this.počátek = počátek;
    }



//== ABSTRAKTNÍ METODY =========================================================
//== PŘÍSTUPOVÉ METODY VLASTNOSTÍ INSTANCÍ =====================================

    /***************************************************************************
     * Vrátí počáteční blok dané cesty.
     *
     * @return Počáteční blok cesty
     */
    public PoleCesty getPočátek()
    {
        return počátek;
    }


    /***************************************************************************
     * Vrátí relativní políčkovou pozici počátku okruhu,
     * tj. pozici startovního pole měřenou relativně k počátku oblasti,
     * kterou daný okruh zaujímá, měřenou v políčcích.
     *
     * @return Relativní políčková pozicePole počátku okruhu
     */
    public Pozice getRPPozicePočátku()
    {
        if (rpPozicePočátku == null) {
            getPRozměr();   //Metoda zjišťuje současně pRozměr i rpPočátek
        }
        return rpPozicePočátku;
    }


    /***************************************************************************
     * Vrátí políčkový rozměr okruhu, (rozměr měřený v políčcích), čímž se
     * rozumí rozměr nejmenšího obdélníku, do nějž se okruh vejde.
     * Metoda počítá rozměr a relativní políčkovou pozici počátku, ale pouze
     * při prvním zavolání. Při dalších voláních si již tyto údaje bere
     * z atributu, takže další dotazy již program nezdržují.
     *
     * @return Požadovaný rozměr
     */
    public Rozměr getPRozměr()
    {
        if (pRozměr == null) {
            PoleCesty pole       = počátek;
            Pozice    pozicePole = počátek.getPozice();
            int minx = pozicePole.x;
            int miny = pozicePole.y;
            int maxx = minx;
            int maxy = miny;
            for (;;) {
                //Příprava dalších hodnot parametrů
                pole       = pole.getDalší();
                pozicePole = pole.getPozice();
                if (pole == počátek) {  //Okruh je již oběhnut
                    break;              //---------->
                }
                //Test vodorovné souřadnice
                if (pozicePole.x < minx) {
                    minx = pozicePole.x;
                } else if (pozicePole.x > maxx) {
                    maxx = pozicePole.x;
                }
                //Test svislé souřadnice
                if (pozicePole.y < miny) {
                    miny = pozicePole.y;
                } else if (pozicePole.y > maxy) {
                    maxy = pozicePole.y;
                }
            }
            //Krajní bodové souřadnice známe, můžeme je přepočítat na políčkové
            int modul = pole.getModul();
            pRozměr = new Rozměr((maxx - minx)/modul + 1,
                                 (maxy - miny)/modul + 1);
            rpPozicePočátku = new Pozice((pozicePole.x - minx)/modul,
                                         (pozicePole.y - miny)/modul);
        }
        return pRozměr;
    }


    /***************************************************************************
     * Vrátí přepravku se souřadnicemi instance.
     *
     * @return  Přepravka se souřadnicemi instance
     */
     @Override
    public Pozice getPozice()
    {
        if (this.poziceOkruhu == null) {
            getPRozměr();   //Metoda zjišťuje současně pRozměr i rpPočátek
        }
        int modul = getModul();
        Pozice pozicePočátku = počátek.getPozice();
        int x = pozicePočátku.x  -  rpPozicePočátku.x * modul;
        int y = pozicePočátku.y  -  rpPozicePočátku.y * modul;
        this.poziceOkruhu = new Pozice(x, y);
        return this.poziceOkruhu;
    }


    /***************************************************************************
     * Nastaví novou pozici instance.
     *
     * @param nováPozice  Nová pozicePole instance
     */
    @Override
    public void setPozice(Pozice nováPozice)
    {
        setPozice(nováPozice.x, nováPozice.y);
    }


    /***************************************************************************
     * Nastaví novou pozici instance.
     *
     * @param x   Nová x-ová pozicePole instance
     * @param y   Nová y-ová pozicePole instance
     */
    @Override
    public void setPozice(int x, int y)
    {
        int dx = x  -  poziceOkruhu.x;
        int dy = y  -  poziceOkruhu.y;
        PoleCesty pole = počátek;
        Pozice pozicePole;
        SP.nekresli(); {
            do {
                pozicePole = pole.getPozice();
                pole.setPozice(pozicePole.x + dx, pozicePole.y + dy);
                pole = pole.getDalší();
            } while (pole != počátek);
            this.poziceOkruhu = new Pozice(x, y);
        } SP.vraťKresli();
    }


    /***************************************************************************
     * Vrátí základní délku, od níž se odvozují všechny rozměry objektu.
     *
     * @return Velikost modulu
     */
    @Override
    public int getModul() {
        return počátek.getModul();
    }


    /***************************************************************************
     * Nastaví nový základní rozměr objektu,
     * od nějž se odvozují jeho ostatní rozměry.
     *
     * @param novýModul Nově nastavovaný novýModul
     */
    @Override
    public void setModul(int novýModul) {
        if (this.poziceOkruhu == null) {
            getPozice();
        }
        //Spočte současnou políčkovou pozici
        int starýModul = getModul();
        
        SP.nekresli(); {
            //Nastaví všem polím nový novýModul a pozici
            PoleCesty pole = počátek;
            do {
                Pozice pozicePole = pole.getPozice();

                //Zjistí jejich políčkovou pozici
                int px = pozicePole.x / starýModul;
                int py = pozicePole.y / starýModul;

                //Z políčkové pozicePole odvodí bodovou pozici při novém modulu
                pole.setPozice(px*novýModul, py*novýModul);

                pole.setModul(novýModul);
                pole = pole.getDalší();
            } while (pole != počátek);
        } SP.vraťKresli();
    }


    /***************************************************************************
     * Prostřednictvím dodaného kreslítka vykreslí obraz své instance.
     *
     * @param kreslítko Kreslítko, které nakreslí instanci
     */
    @Override
    public void nakresli(Kreslítko kreslítko)
    {
        PoleCesty pole = počátek;
        do {
            pole.nakresli(kreslítko);
            pole = pole.getDalší();
        } while (pole != počátek);
    }



//== OSTATNÍ NESOUKROMÉ METODY INSTANCÍ ========================================

    /***************************************************************************
     * Vrací textovou reprezentaci (podpis) dané instance
     * používanou především k ladicím účelům.
     *
     * @return Požadovaná textová reprezentace
     */
    @Override
    public String toString()
    {
        return "Okruh_(počátek=" + počátek + ")";
    }



//== SOUKROMÉ A POMOCNÉ METODY TŘÍDY ===========================================
//== SOUKROMÉ A POMOCNÉ METODY INSTANCÍ ========================================
//== INTERNÍ DATOVÉ TYPY =======================================================
//== TESTY A METODA MAIN =======================================================
//
//    /***************************************************************************
//     * Testovací metoda.
//     */
//    public static void test()
//    {
//        Okruh inst = new Okruh();
//    }
//    /** @param args Parametry příkazového řádku - nepoužívané. */
//    public static void main( String[] args )  {  test();  }
}

